From 92f6d8ecc87c4965dab6f0f52024a360b498b3db Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 15 Sep 2025 11:33:16 +0200 Subject: [PATCH] Support using composefs signatures also with bootc commits When using bootc, if you convert a signed ostree commit into an OCI image `rpm-ostree compose container-encapsulate` you end up with a new commit that isn't signed. However, the base commit object, and its commitmeta are still in the image and will end up the repo, and since https://github.com/bootc-dev/bootc/pull/1600 the base commit id is available as the parent commit. So, we change ostree-prepare-root to fall back to using the base commit+commitmeta to find the expected composefs digest if the main commit is not signed. Note: This will only work with ostree-only commits. If you have any layered data, then the content will change, and the composefs digest in the base commit will not match the deployed one. This is expected with such sealed commits though. If you want to layer, either disable sealing, or create a new sealed ostree commit for the new image. --- src/libotcore/otcore-prepare-root.c | 82 ++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 7 deletions(-) diff --git a/src/libotcore/otcore-prepare-root.c b/src/libotcore/otcore-prepare-root.c index 6ca42afc..a628b880 100644 --- a/src/libotcore/otcore-prepare-root.c +++ b/src/libotcore/otcore-prepare-root.c @@ -305,11 +305,41 @@ load_variant (const char *root_mountpoint, const char *digest, const char *exten return TRUE; } +/* For local bootc commit, return the base ostree commit that was used to generate the commit. + */ +static char * +get_base_digest_for_bootc_commit (GVariant *commit) +{ + g_autoptr (GVariant) metadata = g_variant_get_child_value (commit, 0); + + /* Check for ostree.container.image-config to determine if this is a bootc commit */ + const char *image_config = NULL; + if (!g_variant_lookup (metadata, "ostree.container.image-config", "s", &image_config)) + return NULL; + + /* If so, since https://github.com/bootc-dev/bootc/pull/1600, the + * parent commit will be the base ostree commit. */ + + g_autoptr (GVariant) parent_commit_v = g_variant_get_child_value (commit, 1); + if (g_variant_n_children (parent_commit_v) != OSTREE_SHA256_DIGEST_LEN) + return NULL; + + const guint8 *parent_commit_bin = ot_variant_get_data (parent_commit_v, NULL); + if (parent_commit_bin == NULL) + return NULL; + + char *basecommit_digest = g_malloc (OSTREE_SHA256_STRING_LEN + 1); + ot_bin2hex (basecommit_digest, parent_commit_bin, OSTREE_SHA256_DIGEST_LEN); + + return basecommit_digest; +} + // Given a mount point, directly load the .commit object. At the current time this tool // doesn't link to libostree. static gboolean load_commit_for_deploy (const char *root_mountpoint, const char *deploy_path, GVariant **commit_out, - GVariant **commitmeta_out, GError **error) + GVariant **commitmeta_out, GVariant **basecommit_out, + GVariant **basecommitmeta_out, GError **error) { g_autofree char *digest = g_path_get_basename (deploy_path); char *dot = strchr (digest, '.'); @@ -318,6 +348,8 @@ load_commit_for_deploy (const char *root_mountpoint, const char *deploy_path, GV g_autoptr (GVariant) commit_v = NULL; g_autoptr (GVariant) commitmeta_v = NULL; + g_autoptr (GVariant) basecommit_v = NULL; + g_autoptr (GVariant) basecommitmeta_v = NULL; if (!load_variant (root_mountpoint, digest, "commit", OSTREE_COMMIT_GVARIANT_FORMAT, FALSE, &commit_v, error)) @@ -327,8 +359,28 @@ load_commit_for_deploy (const char *root_mountpoint, const char *deploy_path, GV &commitmeta_v, error)) return FALSE; + /* In case the commit is one created by bootc when importing a container, it will not + * be signed. However, we can still look at the base commit which may be signed. + */ + g_autofree char *basecommit_digest = get_base_digest_for_bootc_commit (commit_v); + if (basecommit_digest) + { + if (!load_variant (root_mountpoint, basecommit_digest, "commit", + OSTREE_COMMIT_GVARIANT_FORMAT, TRUE, &basecommit_v, error)) + return FALSE; + + if (basecommit_v != NULL) + { + if (!load_variant (root_mountpoint, basecommit_digest, "commitmeta", + G_VARIANT_TYPE ("a{sv}"), TRUE, &basecommitmeta_v, error)) + return FALSE; + } + } + *commit_out = g_steal_pointer (&commit_v); *commitmeta_out = g_steal_pointer (&commitmeta_v); + *basecommit_out = g_steal_pointer (&basecommit_v); + *basecommitmeta_out = g_steal_pointer (&basecommitmeta_v); return TRUE; } @@ -556,14 +608,30 @@ otcore_mount_rootfs (RootConfig *rootfs_config, GVariantBuilder *metadata_builde if (rootfs_config->is_signed) { const char *composefs_pubkey = rootfs_config->signature_pubkey; - g_autoptr (GVariant) commit = NULL; - g_autoptr (GVariant) commitmeta = NULL; - - if (!load_commit_for_deploy (root_mountpoint, deploy_path, &commit, &commitmeta, - error)) + g_autoptr (GVariant) main_commit = NULL; + g_autoptr (GVariant) main_commitmeta = NULL; + g_autoptr (GVariant) base_commit = NULL; + g_autoptr (GVariant) base_commitmeta = NULL; + GVariant *commit = NULL; + GVariant *commitmeta = NULL; + + if (!load_commit_for_deploy (root_mountpoint, deploy_path, &main_commit, &main_commitmeta, + &base_commit, &base_commitmeta, error)) return glnx_prefix_error (error, "Error loading signatures from repo"); - if (commitmeta == NULL) + if (main_commitmeta != NULL) + { + commit = main_commit; + commitmeta = main_commitmeta; + } + else if (base_commitmeta != NULL) + { + ot_journal_print (LOG_INFO, + "composefs+ostree: Validating composefs using bootc base commit"); + commit = base_commit; + commitmeta = base_commitmeta; + } + else return glnx_throw (error, "No commitmeta for deploy %s", deploy_path); g_autoptr (GVariant) signatures = g_variant_lookup_value ( -- 2.30.2